using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MKDS_Course_Editor.GCN.Sound
{
    class WSYS
    {
        public class AWFileContent
        {
            public string name;
            public List<byte[]> SubFiles;
            public AWFileContent(string AWName, List<byte[]> Content)
            {
                name = AWName;
                SubFiles = Content;
            }
        }

        public static List<AWFileContent> ReadWS(byte[] file, string awdir)
        {
            List<AWFileContent> ar = new List<AWFileContent>();
            EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(file), Endianness.BigEndian);
            if (er.ReadString(Encoding.ASCII, 4) == "WSYS")
            {
                er.BaseStream.Seek(12, SeekOrigin.Current);
                int winfoffset = er.ReadInt32();
                er.BaseStream.Position = winfoffset;
                if (er.ReadString(Encoding.ASCII, 4) == "WINF")
                {
                    int AWCount = er.ReadInt32();
                    for (int i = 0; i < AWCount; i++)
                    {
                        er.BaseStream.Position = winfoffset + 8 + (i * 4);
                        int aw_name = er.ReadInt32();
                        int tableoffset = aw_name + 112;
                        er.BaseStream.Position = aw_name;
                        string name = er.ReadStringNT(Encoding.ASCII);
                        er.BaseStream.Position = tableoffset;
                        EndianBinaryReader aw = new EndianBinaryReader(File.OpenRead(awdir + @"\" + name), Endianness.BigEndian);
                        int wavcount = er.ReadInt32();
                        List<byte[]> wavs = new List<byte[]>();
                        for (int j = 0; j < wavcount; j++)
                        {
                            er.BaseStream.Position = tableoffset + 4 + j * 4;
                            int waveentryoffset = er.ReadInt32();
                            er.BaseStream.Position = waveentryoffset;
                            int unknown2 = er.ReadInt32();
                            byte[] buf = er.ReadBytes(4);
                            int srate = ((buf[1] << 8) | buf[2]) / 2;
                            int afcoffset = er.ReadInt32();
                            int afcsize = er.ReadInt32();
                            int unknown = er.ReadInt32();
                            if (unknown2 != -16630784)
                            {
                                wavs.Add(dumpAFC(aw, afcoffset, afcsize, srate));
                            }
                            else
                            {
                                 aw.BaseStream.Position =afcoffset;
                                 byte[] b = aw.ReadBytes(afcsize);
                                 b = PCM8(b);
                                 SDAT.WAV.ArchivoWAV w = SDAT.WAV.GenerarWAVPCM(1, (uint)srate, 8, b);
                                 wavs.Add(SDAT.WAV.EscribirArchivo(w));
                            }
                            //File.Create(@"C:\testaw\" + name + "_" + j.ToString() + ".wav").Close();
                            //File.WriteAllBytes(@"C:\testaw\" + name + "_" + j.ToString()+".wav", wavs.Last());
                        }
                        ar.Add(new AWFileContent(name, wavs));
                        aw.Close();
                    }
                }
                else
                {
                    System.Windows.Forms.MessageBox.Show("Error");
                }
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Error");
            }
            er.Close();
            return ar;
        }
        private static byte[] PCM8(byte[] data)
        {
            byte[] resul = new byte[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                resul[i] = unchecked((byte)(data[i] ^ 0x80));
            }

            return resul;
        }
        private static byte[] dumpAFC(EndianBinaryReader er, int offset, int size, int srate)
        {
            long oldpos;
            byte[] inbuf = new byte[9];
            short[] outbuf = new short[16];
            MemoryStream outs = new MemoryStream();
            EndianBinaryWriter outfile = new EndianBinaryWriter(outs, Endianness.LittleEndian);
            long sizeleft;
            int outsize, outsizetotal;
            short hist = 0;
            short hist2 = 0;

            byte[] wavhead = new byte[] {
		0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00,  0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20,
		0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61,  0x00, 0x00, 0x00, 0x00
	};

            oldpos = er.BaseStream.Position;
            if (oldpos < 0) return null;

            outsize = size / 9 * 16 * 2;
            outsizetotal = outsize + 8;
            outfile.Write(wavhead, 0, 44);
            outfile.BaseStream.Position = 4;
            outfile.Write(outsizetotal);
            outfile.BaseStream.Position = 40;
            outfile.Write(outsize);
            outfile.BaseStream.Position = 24;
            outfile.Write(srate);
            outfile.BaseStream.Position = 28;
            outfile.Write(srate * 2);
            outfile.BaseStream.Position = 44;
            er.BaseStream.Position = offset;
            AFCDecoder.Class1 c = new AFCDecoder.Class1();
            if (!(outsizetotal > 700000))
            {
                for (sizeleft = size; sizeleft >= 9; sizeleft -= 9)
                {
                    inbuf = er.ReadBytes(9);
                    

                    unsafe
                    {
                        byte* src = stackalloc byte[9];
                        for (int i = 0; i < 9; i++)
                        {
                            src[i] = (byte)inbuf[i];
                        }
                        short* dst = stackalloc short[16];

                        c.AFCdecodebuffer(src, dst, &hist, &hist2);

                        for (int i = 0; i < 16; i++)
                        {
                            outbuf[i] = dst[i];
                        }
                    }

                    //AFCdecodebuffer(inbuf, outbuf, hist, hist2);

                    outfile.Write(outbuf, 0, 16);
                }
            }
            byte[] output = outs.ToArray();
            outfile.Close();

            er.BaseStream.Position = oldpos;

            return output;
        }
        /*private static void AFCdecodebuffer
(
 byte[] input, /* location of encoded source samples /
 short[] output,   /* location of destination buffer (16 bits / sample) /
 ref short histp,
 ref short hist2p
)
        {
            int sample;
            short[] nibbles = new short[16];
            int i, j;
            byte[] src;
            short[] dst;
            short idx;
            short delta;
            short hist = histp;
            short hist2 = hist2p;

            dst = output;
            src = input;

            int s = 0;
            delta = (short)(1 << (((src[s]) >> 4) & 0xf));
            idx = (short)((src[s]) & 0xf);

            //src[s] >>= 8;
            s++;
            for (i = 0; i < 16; i += 2)
            {
                j = (src[s] & 255) >> 4;
                nibbles[i] = (short)j;
                j = src[s] & 255 & 15;
                nibbles[i + 1] = (short)j;
                s++;
            }

            for (i = 0; i < 16; i = i + 1)
            {
                if (nibbles[i] >= 8)
                {
                    nibbles[i] = (short)(nibbles[i] - (short)(16));
                }
            }
            int s2 = 0;
            for (i = 0; i < 16; i++)
            {

                sample = (delta * nibbles[i]) << 11;
                sample += (int)(((long)hist * afccoef[idx][0]) + ((long)hist2 * afccoef[idx][1]));
                sample = sample >> 11;

                if (sample > 32767)
                {
                    sample = 32767;
                }
                if (sample < -32768)
                {
                    sample = -32768;
                }

                dst[s2] = (short)sample;
                //dst = dst + 2;
                s2 ++;

                hist2 = hist;
                hist = (short)sample;

            }
            //for (i = 0; i < 16; i++)
            //{
                //output = dst;
            //}

            histp = hist;
            hist2p = hist2;

            return;
        }
        
        private static ushort[][] afccoef = new ushort[16][]
{new ushort[]{0,0},
new ushort[]{0x0800,0},
new ushort[]{0,0x0800},
new ushort[]{0x0400,0x0400},
new ushort[]{0x1000,0xf800},
new ushort[]{0x0e00,0xfa00},
new ushort[]{0x0c00,0xfc00},
new ushort[]{0x1200,0xf600},
new ushort[]{0x1068,0xf738},
new ushort[]{0x12c0,0xf704},
new ushort[]{0x1400,0xf400},
new ushort[]{0x0800,0xf800},
new ushort[]{0x0400,0xfc00},
new ushort[]{0xfc00,0x0400},
new ushort[]{0xfc00,0},
new ushort[]{0xf800,0}};
    }*/
        private readonly static ushort[,] afccoef = { { 0, 0 }, { 0x0800, 0 }, { 0, 0x0800 }, { 0x0400, 0x0400 }, { 0x1000, 0xf800 }, { 0x0e00, 0xfa00 }, { 0x0c00, 0xfc00 }, { 0x1200, 0xf600 }, { 0x1068, 0xf738 }, { 0x12c0, 0xf704 }, { 0x1400, 0xf400 }, { 0x0800, 0xf800 }, { 0x0400, 0xfc00 }, { 0xfc00, 0x0400 }, { 0xfc00, 0 }, { 0xf800, 0 } };

        private unsafe static void AFCdecodebuffer(byte[] input, short[] @out, short histp, short hist2p) // location of destination buffer (16 bits / sample) -  location of encoded source samples
        {
            int sample;
            short[] nibbles = new short[16];
            int i;
            int j;
            //C++ TO C# CONVERTER TODO TASK: Pointer arithmetic is detected on this variable, so pointers on this variable are left unchanged.
            short[] dst;
            short idx;
            short delta;
            short hist = histp;
            short hist2 = hist2p;

            dst = @out;

            byte* src = stackalloc byte[9];
            for (i = 0; i < 9; i++)
            {
                src[i] = input[i];
            }
            delta = (short)(1 << (((*src) >> 4) & 0xf));
            idx = (short)((*src) & 0xf);

            src++;

            for (i = 0; i < 16; i = i + 2)
            {
                j = (*src & 255) >> 4;
                nibbles[i] = (short)j;
                j = *src & 255 & 15;
                nibbles[i + 1] = (short)j;
                src++;
            }

            for (i = 0; i < 16; i = i + 1)
            {
                if (nibbles[i] >= 8)
                    nibbles[i] = (short)(nibbles[i] - 16);
            }

            for (i = 0; i < 16; i = i + 1)
            {

                sample = (delta * nibbles[i]) << 11;
                sample += (int)(((int)hist * afccoef[idx, 0]) + ((int)hist2 * afccoef[idx, 1]));
                sample = sample >> 11;

                if (sample > 32767)
                {
                    sample = 32767;
                }
                if (sample < -32768)
                {
                    sample = -32768;
                }

                dst[i] = (short)sample;

                hist2 = hist;
                hist = (short)sample;
            }

            histp = hist;
            hist2p = hist2;
        }
    }
}
